#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
XML处理模块
用于解析和生成CEGUI的XML布局文件和Imageset文件
"""

import os
import xml.etree.ElementTree as ET
from xml.dom import minidom
from PyQt5.QtCore import QObject, pyqtSignal, QFileInfo
from PyQt5.QtWidgets import QFileDialog, QMessageBox
import glob

class XMLHandler(QObject):
    """XML处理类"""
    
    # 定义信号
    layout_loaded = pyqtSignal(object)
    imageset_loaded = pyqtSignal(object)
    layouts_directory_scanned = pyqtSignal(list)  # 扫描布局目录完成信号，传递所有布局文件列表
    imagesets_directory_scanned = pyqtSignal(list)  # 扫描图集目录完成信号，传递所有图集文件列表
    
    def __init__(self):
        super(XMLHandler, self).__init__()
        self.current_layout = None
        self.current_imageset = None
        self.imagesets = {}  # 存储所有加载的imageset
        self.imageset_images = {}  # 存储imageset对应的图像文件路径
        self.imageset_search_dirs = []  # 存储用户自定义的搜索目录
        self.root_directory = ""  # 存储根目录路径
        self.layout_files = []  # 存储扫描到的所有布局文件
        self.imageset_files = []  # 存储扫描到的所有图集文件
    
    def set_root_directory(self, directory):
        """设置根目录，并扫描其中的layouts和imagesets子目录"""
        if not os.path.isdir(directory):
            print(f"无效的目录: {directory}")
            return False
        
        self.root_directory = directory
        print(f"已设置根目录: {directory}")
        
        # 扫描布局文件和图集文件
        self.scan_layouts_directory()
        self.scan_imagesets_directory()
        
        # 将图集目录添加到搜索目录
        imagesets_dir = os.path.join(directory, "imagesets")
        if os.path.isdir(imagesets_dir) and imagesets_dir not in self.imageset_search_dirs:
            self.imageset_search_dirs.append(imagesets_dir)
        
        return True
    
    def scan_layouts_directory(self):
        """扫描layouts子目录中的所有布局文件"""
        self.layout_files = []
        layouts_dir = os.path.join(self.root_directory, "layouts")
        
        if not os.path.isdir(layouts_dir):
            print(f"layouts目录不存在: {layouts_dir}")
            self.layouts_directory_scanned.emit([])
            return []
        
        # 扫描所有xml文件
        for file_path in glob.glob(os.path.join(layouts_dir, "**/*.xml"), recursive=True):
            try:
                # 检查是否是有效的布局文件
                tree = ET.parse(file_path)
                root = tree.getroot()
                if root.tag == 'GUILayout':
                    rel_path = os.path.relpath(file_path, self.root_directory)
                    self.layout_files.append({
                        'path': file_path,
                        'name': os.path.basename(file_path),
                        'rel_path': rel_path
                    })
                    print(f"找到布局文件: {rel_path}")
            except Exception as e:
                print(f"解析布局文件出错 {file_path}: {e}")
        
        print(f"共扫描到 {len(self.layout_files)} 个布局文件")
        self.layouts_directory_scanned.emit(self.layout_files)
        return self.layout_files
    
    def scan_imagesets_directory(self):
        """扫描imagesets子目录中的所有图集文件"""
        self.imageset_files = []
        imagesets_dir = os.path.join(self.root_directory, "imagesets")
        
        if not os.path.isdir(imagesets_dir):
            print(f"imagesets目录不存在: {imagesets_dir}")
            self.imagesets_directory_scanned.emit([])
            return []
        
        # 扫描所有imageset文件和xml文件
        for pattern in ["**/*.imageset", "**/*.xml"]:
            for file_path in glob.glob(os.path.join(imagesets_dir, pattern), recursive=True):
                try:
                    # 检查是否是有效的图集文件
                    tree = ET.parse(file_path)
                    root = tree.getroot()
                    if root.tag == 'Imageset' and 'Name' in root.attrib:
                        imageset_name = root.get('Name')
                        rel_path = os.path.relpath(file_path, self.root_directory)
                        self.imageset_files.append({
                            'path': file_path,
                            'name': imageset_name,
                            'file_name': os.path.basename(file_path),
                            'rel_path': rel_path
                        })
                        print(f"找到图集文件: {rel_path} (Name: {imageset_name})")
                except Exception as e:
                    print(f"解析图集文件出错 {file_path}: {e}")
        
        print(f"共扫描到 {len(self.imageset_files)} 个图集文件")
        self.imagesets_directory_scanned.emit(self.imageset_files)
        return self.imageset_files
    
    def preload_all_imagesets(self):
        """预加载所有扫描到的图集文件"""
        count = 0
        for imageset_info in self.imageset_files:
            if self.load_imageset(imageset_info['path']):
                count += 1
        
        print(f"已预加载 {count} 个图集文件")
        return count
    
    def find_imageset_by_name(self, name):
        """根据名称查找图集文件"""
        for imageset_info in self.imageset_files:
            if imageset_info['name'] == name:
                return imageset_info
        return None
    
    def set_imageset_search_dirs(self, dirs):
        """设置imageset搜索目录列表"""
        if isinstance(dirs, str):
            # 如果是单个目录字符串，转换为列表
            self.imageset_search_dirs = [dirs]
        else:
            # 否则假设是目录列表
            self.imageset_search_dirs = dirs
        print(f"已设置imageset搜索目录: {self.imageset_search_dirs}")
    
    def add_imageset_search_dir(self, dir_path):
        """添加一个imageset搜索目录"""
        if dir_path and os.path.isdir(dir_path) and dir_path not in self.imageset_search_dirs:
            self.imageset_search_dirs.append(dir_path)
            print(f"已添加imageset搜索目录: {dir_path}")
            return True
        return False
    
    def load_layout(self, file_path):
        """加载布局文件"""
        try:
            tree = ET.parse(file_path)
            self.current_layout = tree
            root = tree.getroot()
            
            # 记录文件路径，用于后续处理和相关路径解析
            self.current_layout_path = file_path
            
            # 自动查找并加载布局中引用的图集
            self.auto_load_imagesets(root, os.path.dirname(file_path))
            
            self.layout_loaded.emit(root)
            return root
        except Exception as e:
            print(f"加载布局文件出错: {e}")
            return None
    
    def auto_load_imagesets(self, layout_root, base_path):
        """自动查找并加载布局中引用的所有图集"""
        try:
            # 查找所有包含图像引用的属性值
            image_properties = []
            
            # 需要检查的特殊属性名
            image_property_names = [
                'Image', 'olNormalImage', 'olHoverImage', 'olPushedImage',
                'olDisabledImage', 'olSelectedImage', 'olHighlightImage',
                'MouseCursorImage', 'olMouseCursorImage',
                'BackgroundImage',
                'BottomFrameImage',
                'BottomLeftFrameImage',
                'BottomRightFrameImage',
                'LeftFrameImage',
                'RightFrameImage',
                'TopFrameImage',
                'TopLeftFrameImage',
                'TopRightFrameImage'
            ]
            
            # 递归查找所有带有Value属性的Property元素
            def find_image_properties(element):
                # 检查所有Property元素
                for prop in element.findall('Property'):
                    # 检查是否是已知的图像属性
                    if prop.get('Name') in image_property_names:
                        value = prop.get('Value')
                        if value:
                            image_properties.append(value)
                            continue
                    
                    # 检查其他属性的Value是否包含图像引用
                    if 'Value' in prop.attrib:
                        value = prop.get('Value')
                        # 如果Value包含"set:"和"image:"，则认为是图像引用
                        if "set:" in value and "image:" in value:
                            image_properties.append(value)
                
                # 处理子元素
                for child in element.findall('Window'):
                    find_image_properties(child)
            
            # 开始查找
            window = layout_root.find('.//Window')
            if window is not None:
                find_image_properties(window)
            
            #print(f"找到的图像属性: {image_properties}")
            
            # 提取所有不同的图集名称
            imageset_names = set()
            for prop_value in image_properties:
                try:
                    parts = prop_value.split()
                    for part in parts:
                        if part.startswith("set:"):
                            imageset_name = part[4:]  # 去掉"set:"前缀
                            imageset_names.add(imageset_name)
                            #print(f"找到图集引用: {imageset_name}")
                except:
                    pass
            
            print(f"需要加载的图集: {imageset_names}")
            
            # 构建要搜索的目录列表
            search_dirs = [
                os.path.join(base_path, "..", "imagesets"),
                os.path.join(base_path, "imagesets"),
                base_path,
                os.path.dirname(base_path), 
                os.path.join(os.path.dirname(base_path), "imagesets"),
                os.path.join(os.path.dirname(os.path.dirname(base_path)), "imagesets")
            ]
            
            # 添加用户自定义的搜索目录
            search_dirs.extend(self.imageset_search_dirs)
            
            # 如果设置了根目录，确保imagesets子目录被添加到搜索目录
            if self.root_directory:
                imagesets_dir = os.path.join(self.root_directory, "imagesets")
                if os.path.isdir(imagesets_dir) and imagesets_dir not in search_dirs:
                    search_dirs.append(imagesets_dir)
            
            # 去除重复项和不存在的目录
            search_dirs = [d for d in search_dirs if os.path.isdir(d)]
            search_dirs = list(dict.fromkeys(search_dirs))  # 去重
            
            print(f"搜索目录: {search_dirs}")
            
            # 尝试加载所有找到的图集
            for name in imageset_names:
                # 如果图集已经加载，跳过
                if name in self.imagesets:
                    print(f"图集已加载，跳过: {name}")
                    continue
                
                # 首先从已扫描的图集文件中查找
                imageset_file = None
                imageset_info = self.find_imageset_by_name(name)
                if imageset_info:
                    imageset_file = imageset_info['path']
                    print(f"从扫描列表中找到图集: {name} ({imageset_file})")
                
                # 如果在扫描列表中没找到，则在搜索目录中查找
                if not imageset_file:
                    # 在所有搜索目录中查找.imageset和.xml文件
                    # 首先在每个搜索目录中查找精确匹配的文件名
                    for search_dir in search_dirs:
                        # 查找.imageset文件
                        imageset_path = os.path.join(search_dir, f"{name}.imageset")
                        if os.path.isfile(imageset_path):
                            imageset_file = imageset_path
                            break
                        
                        # 查找.xml文件
                        xml_path = os.path.join(search_dir, f"{name}.xml")
                        if os.path.isfile(xml_path):
                            imageset_file = xml_path
                            break
                
                # 如果没找到，则通过搜索目录中所有的.imageset文件内容来查找匹配的图集名称
                if not imageset_file:
                    for search_dir in search_dirs:
                        # 搜索所有.imageset文件
                        imageset_files = glob.glob(os.path.join(search_dir, "*.imageset"))
                        imageset_files.extend(glob.glob(os.path.join(search_dir, "*.xml")))
                        
                        for file_path in imageset_files:
                            try:
                                # 解析文件，检查Imageset的Name属性是否匹配
                                tree = ET.parse(file_path)
                                root = tree.getroot()
                                if root.tag == 'Imageset' and root.get('Name') == name:
                                    imageset_file = file_path
                                    print(f"通过解析内容找到图集 {name}: {file_path}")
                                    break
                            except Exception as e:
                                print(f"解析文件出错 {file_path}: {e}")
                                continue
                        
                        if imageset_file:
                            break
                
                # 加载找到的图集文件
                if imageset_file:
                    print(f"加载图集: {imageset_file}")
                    self.load_imageset(imageset_file)
                else:
                    print(f"未找到图集文件: {name}")
                    
                    # 如果是OiramLook这样的特殊图集，可以考虑创建一个空的图集
                    if name == "OiramLook":
                        self.create_empty_imageset(name)
            
            return True
        except Exception as e:
            print(f"自动加载图集出错: {e}")
            return False
    
    def save_layout(self, root, file_path):
        """保存布局文件
        
        参数:
            root: 布局根元素（ElementTree.Element 或 ElementTree 对象）
            file_path: 保存路径
            
        返回:
            bool: 保存是否成功
        """
        try:
            # 检查root类型并适当处理
            if isinstance(root, str):
                print(f"警告: root参数是字符串而非元素树: {root[:30]}...")
                # 尝试解析字符串为XML
                try:
                    root = ET.fromstring(root)
                except Exception as e:
                    print(f"无法将字符串解析为XML: {e}")
                    return False
            
            # 确保root是Element类型
            if hasattr(root, 'getroot'):  # 如果是ElementTree对象
                root = root.getroot()
                
            # 将ElementTree转换为字符串并美化格式
            rough_string = ET.tostring(root, 'utf-8')
            reparsed = minidom.parseString(rough_string)
            pretty_xml = reparsed.toprettyxml(indent="  ")
            
            # 写入文件
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(pretty_xml)
            
            print(f"布局文件已成功保存到: {file_path}")
            return True
        except Exception as e:
            print(f"保存布局文件出错: {e}")
            import traceback
            traceback.print_exc()
            return False
    
    def load_imageset(self, file_path):
        """加载Imageset文件"""
        try:
            tree = ET.parse(file_path)
            root = tree.getroot()
            imageset_name = root.get('Name')
            image_file = root.get('Imagefile')
            
            # 存储imageset信息
            images = {}
            for image in root.findall('Image'):
                img_name = image.get('Name')
                img_info = {
                    'XPos': int(image.get('XPos', 0)),
                    'YPos': int(image.get('YPos', 0)),
                    'Width': int(image.get('Width', 0)),
                    'Height': int(image.get('Height', 0)),
                    'XOffset': int(image.get('XOffset', 0)),
                    'YOffset': int(image.get('YOffset', 0))
                }
                images[img_name] = img_info
            
            self.imagesets[imageset_name] = {
                'Imagefile': image_file,
                'ResourceGroup': root.get('ResourceGroup', ''),
                'NativeHorzRes': int(root.get('NativeHorzRes', 640)),
                'NativeVertRes': int(root.get('NativeVertRes', 480)),
                'AutoScaled': root.get('AutoScaled', 'false').lower() == 'true',
                'Images': images
            }
            
            # 检查图像文件是否存在
            self.check_image_file(imageset_name, file_path)
            
            self.current_imageset = imageset_name
            self.imageset_loaded.emit(self.imagesets[imageset_name])
            return self.imagesets[imageset_name]
        except Exception as e:
            print(f"加载Imageset文件出错: {e}")
            return None
    
    def check_image_file(self, imageset_name, imageset_file_path):
        """检查图像文件是否存在，如果不存在则提示用户指定"""
        if imageset_name not in self.imagesets:
            return
        
        image_file = self.imagesets[imageset_name]['Imagefile']
        print(f"检查图集 {imageset_name} 的图像文件: {image_file}")
        
        # 尝试在多个位置查找图像文件
        imageset_dir = os.path.dirname(imageset_file_path)
        base_dir = os.path.dirname(imageset_dir)
        image_basename = os.path.basename(image_file)
        
        # 构建要搜索的目录列表
        search_dirs = [
            imageset_dir,
            base_dir,
            os.path.join(base_dir, "imagesets"),
            os.path.join(os.path.dirname(base_dir), "imagesets"),
            os.path.join(base_dir, ".."),
            os.path.join(base_dir, "..", "imagesets")
        ]
        
        # 添加用户自定义的搜索目录
        search_dirs.extend(self.imageset_search_dirs)
        
        # 去除重复项和不存在的目录
        search_dirs = [d for d in search_dirs if os.path.isdir(d)]
        search_dirs = list(dict.fromkeys(search_dirs))  # 去重
        
        # 创建可能的路径列表
        potential_paths = [
            os.path.join(imageset_dir, image_file),  # 图集文件所在目录
            os.path.join(base_dir, image_file),      # 父目录
            image_file,                              # 绝对路径
            
            # 如果图像文件路径是以"gamedata\"开头的相对路径
            os.path.join(base_dir, image_file.replace("gamedata\\", "")),
            os.path.join(base_dir, image_file.replace("gamedata/", "")),
            os.path.join(base_dir, "..", image_file),
            os.path.join(base_dir, "..", image_file.replace("gamedata\\", "")),
            os.path.join(base_dir, "..", image_file.replace("gamedata/", "")),
            
            # 额外尝试的路径
            os.path.join(base_dir, "imagesets", image_basename),
            os.path.join(imageset_dir, image_basename),
            os.path.join(os.path.dirname(base_dir), "imagesets", image_basename),
            
            # 使用imageset_name作为图像文件名的情况
            os.path.join(imageset_dir, f"{imageset_name}.png"),
            os.path.join(base_dir, "imagesets", f"{imageset_name}.png"),
            os.path.join(base_dir, f"{imageset_name}.png")
        ]
        
        # 添加自定义搜索目录
        for search_dir in self.imageset_search_dirs:
            # 完整路径
            potential_paths.append(os.path.join(search_dir, image_file))
            # 只用文件名
            potential_paths.append(os.path.join(search_dir, image_basename))
            # 替换gamedata路径
            potential_paths.append(os.path.join(search_dir, image_file.replace("gamedata\\", "")))
            potential_paths.append(os.path.join(search_dir, image_file.replace("gamedata/", "")))
            # 使用imageset名称
            potential_paths.append(os.path.join(search_dir, f"{imageset_name}.png"))
            
            # 尝试在图像子目录中查找
            img_subdir = os.path.join(search_dir, "images")
            if os.path.isdir(img_subdir):
                potential_paths.append(os.path.join(img_subdir, image_basename))
                potential_paths.append(os.path.join(img_subdir, f"{imageset_name}.png"))
        
        # 如果文件路径包含父目录，尝试从不同的相对位置查找
        if '\\' in image_file or '/' in image_file:
            # 规范化路径分隔符
            norm_path = image_file.replace('\\', '/') 
            path_parts = norm_path.split('/')
            
            # 尝试从不同的根目录开始查找
            if len(path_parts) > 1:
                for search_dir in search_dirs:
                    for i in range(len(path_parts)-1):
                        # 从不同的层次开始查找
                        partial_path = '/'.join(path_parts[i:])
                        potential_paths.append(os.path.join(search_dir, partial_path))
        
        # 检查所有可能的路径
        image_path = None
        for path in potential_paths:
            norm_path = os.path.normpath(path)  # 规范化路径
            if os.path.isfile(norm_path):
                print(f"找到图像文件: {norm_path}")
                image_path = norm_path
                break
        
        # 如果找不到图像文件，提示用户指定
        if not image_path:
            msg_box = QMessageBox()
            msg_box.setWindowTitle("图像文件未找到")
            msg_box.setText(f"无法找到图像文件: {image_file}")
            msg_box.setInformativeText(f"图集 '{imageset_name}' 需要图像文件 '{image_file}'，是否手动指定图像文件位置？")
            msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msg_box.setDefaultButton(QMessageBox.Yes)
            
            if msg_box.exec_() == QMessageBox.Yes:
                image_path, _ = QFileDialog.getOpenFileName(
                    None, 
                    f"选择图像文件 {image_file}", 
                    os.path.dirname(imageset_file_path), 
                    "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif *.tga)"
                )
        
        # 保存找到的图像路径
        if image_path:
            self.imageset_images[imageset_name] = image_path
            print(f"设置图集 '{imageset_name}' 的图像路径: {image_path}")
            return True
        
        return False
    
    def save_imageset(self, file_path, imageset_name):
        """保存Imageset文件"""
        try:
            if imageset_name not in self.imagesets:
                print(f"Imageset {imageset_name} 不存在")
                return False
            
            imageset_data = self.imagesets[imageset_name]
            
            # 创建根元素
            root = ET.Element('Imageset')
            root.set('Imagefile', imageset_data['Imagefile'])
            root.set('ResourceGroup', imageset_data['ResourceGroup'])
            root.set('Name', imageset_name)
            root.set('NativeHorzRes', str(imageset_data['NativeHorzRes']))
            root.set('NativeVertRes', str(imageset_data['NativeVertRes']))
            root.set('AutoScaled', 'true' if imageset_data['AutoScaled'] else 'false')
            
            # 添加图像元素
            for img_name, img_info in imageset_data['Images'].items():
                img_elem = ET.SubElement(root, 'Image')
                img_elem.set('Name', img_name)
                img_elem.set('XPos', str(img_info['XPos']))
                img_elem.set('YPos', str(img_info['YPos']))
                img_elem.set('Width', str(img_info['Width']))
                img_elem.set('Height', str(img_info['Height']))
                img_elem.set('XOffset', str(img_info['XOffset']))
                img_elem.set('YOffset', str(img_info['YOffset']))
            
            # 写入文件
            tree = ET.ElementTree(root)
            rough_string = ET.tostring(root, 'utf-8')
            reparsed = minidom.parseString(rough_string)
            pretty_xml = reparsed.toprettyxml(indent="  ")
            
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(pretty_xml)
            return True
        except Exception as e:
            print(f"保存Imageset文件出错: {e}")
            return False
    
    def create_property_element(self, name, value):
        """创建Property元素"""
        prop = ET.Element('Property')
        prop.set('Name', name)
        prop.set('Value', value)
        return prop
    
    def create_window_element(self, window_type, window_name):
        """创建Window元素"""
        window = ET.Element('Window')
        window.set('Type', window_type)
        window.set('Name', window_name)
        return window
    
    def get_image_info(self, imageset_name, image_name):
        """获取指定图像的信息"""
        if imageset_name in self.imagesets and image_name in self.imagesets[imageset_name]['Images']:
            return self.imagesets[imageset_name]['Images'][image_name]
        return None
    
    def get_image_path(self, imageset_name):
        """获取imageset对应的图像文件路径"""
        if imageset_name in self.imageset_images:
            return self.imageset_images[imageset_name]
        return None
    
    def create_empty_imageset(self, name):
        """创建一个空的图集，用于处理特殊情况"""
        print(f"创建空的图集: {name}")
        self.imagesets[name] = {
            'Imagefile': f"{name}.png",
            'ResourceGroup': '',
            'NativeHorzRes': 800,
            'NativeVertRes': 600,
            'AutoScaled': False,
            'Images': {
                'MouseArrow': {
                    'XPos': 0,
                    'YPos': 0,
                    'Width': 32,
                    'Height': 32,
                    'XOffset': 0,
                    'YOffset': 0
                }
            }
        }
        self.current_imageset = name
        self.imageset_loaded.emit(self.imagesets[name])
    
    def add_root_directory(self, directory):
        """设置根目录，并扫描其中的layouts和imagesets子目录"""
        return self.set_root_directory(directory) 